<?php

namespace App\Kuafu\WaiMai\Channel;

use App\Kuafu\WaiMai\Enum\WaiMaiCacheEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Service\ElmWaiMaiOrderService;
use App\Kuafu\WaiMai\Service\MtWaiMaiOrderService;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use KuafuComposer\ApiTime\Service\ApiTimeService;

class MtWaiMai extends BaseWaiMai
{
    const TIME_OUT = 10;

    private string $host       = 'https://waimaiopen.meituan.com';
    private string $app_id     = '6325';
    private string $app_secret = '2b3a27d8313b012aa2a5ec86944e60aa';
    private string $thirdShopId;
    private int    $shopId;

    public function __construct(string $thirdShopId, int $shopId)
    {
        parent::__construct();

        $this->thirdShopId = $thirdShopId;
        $this->shopId      = $shopId;
    }

    /**
     * 获取门店ids
     * @return array
     */
    public function getShopIds(): array
    {
        $url    = $this->host . '/api/v1/poi/getids';
        $params = [];

        $s         = $this->curlCommon($url, $params, 'get');
        $s['data'] = $s['oriData'];
        return $s;
    }

    /**
     * 批量获取门店详情
     * @param array $shopIds
     * @return array
     */
    public function getStoreDetails(array $shopIds): array
    {
        $url    = $this->host . '/api/v1/poi/mget';
        $params = [
            'app_poi_codes' => implode(',', $shopIds),
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    /**
     * 查询单个店铺详情
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getShop()
    {
        // 先校验这个店铺有没有绑定
        if (!in_array($this->shopId, $this->getShopIds()['data'])) {
            return $this->returnFormatData(false, '', "该shopId:{$this->shopId}未在美团商家后台绑定", []);
        }

        $url    = $this->host . '/api/v1/poi/mget';
        $params = [
            'app_poi_codes' => $this->shopId,
        ];

        $s            = $this->curlCommon($url, $params, 'get');
        $s['oriData'] = $s['oriData'][0] ?? [];
        $s['data']    = [
            'name' => $s['oriData']['name'] ?? '',
            'phones' => [$s['oriData']['phone'] ?? ''],
        ];
        return $s;
    }

    /**
     * 门店休息
     * @return array
     */
    public function closeStore(): array
    {
        $url    = $this->host . '/api/v1/poi/close';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 门店营业
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function openStore(): array
    {
        $url    = $this->host . '/api/v1/poi/open';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 门店上线
     * @return array
     */
    public function onlineStore(): array
    {
        $url    = $this->host . '/api/v1/poi/online';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 门店下线 - 不能随便用！！！
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function offlineStore(): array
    {
        $url    = $this->host . '/api/v1/poi/offline';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 更新门店公告
     * @param string $promotionInfo
     * @return array
     */
    public function updatePromoteInfo(string $promotionInfo): array
    {
        $url    = $this->host . '/api/v1/poi/updatepromoteinfo';
        $params = [
            'app_poi_code'   => $this->shopId,
            'promotion_info' => $promotionInfo,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 获取门店品类列表
     * @return array
     */
    public function tagList(): array
    {
        $url    = $this->host . '/api/v1/poiTag/list';
        $params = [];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 更改门店的营业时间
     * @param string $start 7:00
     * @param string $end 9:00
     * @return array
     */
    public function updateShoppingTime(string $start, string $end): array
    {
        $url    = $this->host . '/api/v1/poi/shippingtime/update';
        $params = [
            'app_poi_code'  => $this->shopId,
            'shipping_time' => $start . '-' . $end, // 7:00-9:00,11:30-19:00
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 查询门店是否延迟发配送
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function isDelayPush()
    {
        $url    = $this->host . '/api/v1/poi/logistics/isDelayPush';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 设置门店延迟发配送时间
     * @param int $delaySeconds
     */
    public function setDelayPush(int $delaySeconds)
    {
        $url    = $this->host . '/api/v1/poi/logistics/setDelayPush';
        $params = [
            'app_poi_code'  => $this->shopId,
            'delay_seconds' => $delaySeconds, // 延迟发配送时间／秒，该参数需在300-600秒之间
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    // 根据交易类型查询交易列表
    public function getOrderBill()
    {
        $url    = $this->host . '/api/v1/wm/bill/getOrderBill';
        $params = [
            'app_poi_code'      => $this->shopId,
            'partner_type'      => 0, // 业务类型： 902只能折扣菜，903智能满减
            'bill_charge_types' => '', // 交易类型，多个类型用,隔开
            /*
                41:智能满减节省金额
                42:智能满减节省金额退款
                52:智能折扣菜节省金额
                53:智能折扣菜节省金额退款
                54:智能折扣菜返还金额.
                41，52若出现负值，则代表该活动费用是美团平台代付的费用，由美团承担，商家不承担。
            */
            'start_date'        => 0, // 开始时间，时间戳
            'end_date'          => 0, // 结束时间，时间戳
            'offset'            => 0, // 默认1
            'limit'             => 0, // 默认10
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 商家接单
     * @param int $orderId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function acceptOrder(int $orderId)
    {
        $url    = $this->host . '/api/v1/order/confirm';
        $params = [
            'order_id' => $orderId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if (!$s['status']) {

            $sCode = $s['code'] ?? 0;
            if ($sCode == '808') {
                $s['data']['hasConfirm'] = 1;
            } else {
                $s['data']['hasConfirm'] = 0;
            }

            return $s;
        }

        $s['data'] = [];
        return $s;
    }

    /**
     * 商家取消订单
     * @param int $orderId
     * @param string $reason
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function merchantCancelOrder(int $orderId, string $reason)
    {
        $url    = $this->host . '/api/v1/order/cancel';
        $params = [
            'order_id'    => $orderId,
            'reason'      => $reason,
            'reason_code' => $reasonCode = '1001', // 规范化取消原因code
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    /**
     * 订单确认退款请求
     * @param int $orderId
     * @param string $reason
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function refundAgree(int $orderId)
    {
        $url    = $this->host . '/api/v1/order/refund/agree';
        $params = [
            'order_id' => $orderId,
            'reason'   => '同意',
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    /**
     * 驳回订单退款申请
     * @param int $orderId
     * @param string $reason
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function refundReject(int $orderId, string $reason)
    {
        $url    = $this->host . '/api/v1/order/refund/reject';
        $params = [
            'order_id' => $orderId,
            'reason'   => $reason,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    /**
     * 拉取用户真实手机号
     * @param int $offset
     * @param int $limit
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function batchPullPhoneNumber(int $offset = 0, int $limit = 1000)
    {
        $url    = $this->host . '/api/v1/order/batchPullPhoneNumber';
        $params = [
            'app_poi_code' => $this->shopId,
            'offset'       => $offset,
            'limit'        => $limit,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        return $s;
    }

    /**
     * 拉取骑手真实手机号
     * @param int $offset
     * @param int $limit
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getRiderInfoPhoneNumber(int $offset = 0, int $limit = 1000)
    {
        $url    = $this->host . '/api/v1/order/getRiderInfoPhoneNumber';
        $params = [
            'app_poi_code' => $this->shopId,
            'offset'       => $offset,
            'limit'        => $limit,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    /**
     * 订单详情
     * @param int $orderId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getOrderDetail(int $orderId)
    {
        $url    = $this->host . '/api/v1/order/getOrderDetail';
        $params = [
            'order_id' => $orderId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if (!$s['status']) {
            return $s;
        }

        $s['data'] = [
            'orderStatus' => MtWaiMaiOrderService::convertOrderStatus($s['oriData']['status']),
            'orderId'     => $s['oriData']['order_id'],
            'createdAt'   => $s['oriData']['ctime'],
            'shopId'      => (int)$s['oriData']['app_poi_code'],
        ];
        return $s;
    }

    /**
     * 商家完成出餐
     * @param int $orderId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function finishFoods(int $orderId)
    {
        $url    = $this->host . '/api/v1/order/preparationMealComplete';
        $params = [
            'order_id' => $orderId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        return $s;
    }

    // 拉取推送失败的订单
    // 从第1次推送开始，3分钟未成功。算失败
    public function getErrorOrders(int $startTime = 0, int $endTime = 0, int $offset = 0, int $limit = 100)
    {
        // 默认查1天
        if ($startTime == 0) {
            $startTime = time() - 86400;
        }

        if ($endTime == 0) {
            $endTime = time();
        }

        $url    = $this->host . '/api/v1/order/batchFetchAbnormalOrder';
        $params = [
            'start_time' => $startTime, // 最多只能拉7天的数据
            'end_time'   => $endTime,
            'type'       => 1, // 1推单失败 2超时取消
            'offset'     => $offset,
            'limit'      => $limit,
        ];

        $s         = $this->curlCommon($url, $params, 'post');
        $s['data'] = $s['oriData'];
        return $s;
    }

    /**
     * 根据流水号获取订单ID
     * @param int $dateTime Ymd
     * @param int $daySeq 流水号
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getOrderIdByDaySeq(int $dateTime, int $daySeq)
    {
        $url    = $this->host . '/api/v1/order/getOrderIdByDaySeq';
        $params = [
            'app_poi_code' => $this->shopId,
            'date_time'    => $dateTime,
            'day_seq'      => $daySeq,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if ($s['status']) {
            $s['data'] = [
                'orderId' => $s['oriData']['order_id'] ?? 0,
            ];
        }

        return $s;
    }

    /**
     * 门店下的菜品列表
     * @param int $offset
     * @param int $limit
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getProductPage(int $offset, int $limit)
    {
        if ($limit > 200) {
            $limit = 200;
        }

        $url    = $this->host . '/api/v1/food/list';
        $params = [
            'app_poi_code' => $this->shopId,
            'offset'       => $offset,
            'limit'        => $limit,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if ($s['status']) {
            $data = [];
            foreach ($s['oriData'] as $v) {
                $data[] = [
                    'id'         => $v['mt_spu_id'],
                    'categoryId' => $v['mt_tag_id'],
                    'name'       => $v['name'],
                    'sourceType' => WaiMaiDBEnum::SOURCE_TYPE_MT_WM,
                ];
            }

            $s['data'] = $data;
        }

        return $s;
    }

    /**
     * 获取是否已出餐
     * @param int $orderId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function isFinishFoods(int $orderId)
    {
        $url    = $this->host . '/api/v1/order/getPreparationMealTime';
        $params = [
            'app_poi_code' => $this->shopId,
            'order_id'     => $orderId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if ($s['status']) {
            $s['data'] = [
                'finish' => (int)(bool)($s['oriData']['preparationMealTime'] ?? 0),
            ];
        }
    }
    /*
     * 安心卡
     * @return array
     */
    public function getSecureCard(): array
    {
        $url    = $this->host . '/api/v1/poi/card/queryCard';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'post');
        if (!$s['status']) {
            return $s;
        }

        $cardDetailList = $s['oriData']['cardDetailList'] ?? [];
        foreach ($cardDetailList as $k => $v) {
            if (!isset($v['job']) || !$v['job']) {
                unset($cardDetailList[$k]);
            }
        }
        $cardDetailList = array_values($cardDetailList);

        $s['data'] = [
            'syncOpenReceipt' => $s['oriData']['syncOpenReceipt'], // 是否同步到小票 0不同步 1同步
            'cardDetailList'  => $cardDetailList, // 安心卡人员详情
            'tips'            => $s['oriData']['tips'],
            'cardTime'        => $s['oriData']['cardTime'], // 卡片时间戳，创建时间的当天0点
        ];

        return $s;
    }

    public function getOrderDaySeq(): array
    {
        $url    = $this->host . '/api/v1/order/getOrderDaySeq';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if (!$s['status']) {
            return $s;
        }

        return $s;
    }

    /**
     * 门店坐标信息
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getShoppingList(): array
    {
        $url    = $this->host . '/api/v1/shipping/list';
        $params = [
            'app_poi_code' => $this->shopId,
        ];

        $s = $this->curlCommon($url, $params, 'get');
        if (!$s['status']) {
            return $s;
        }

        return $s;
    }

    /**
     * 统一CURL
     * @param string $url
     * @param array $params
     * @param string $method
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function curlCommon(string $url, array $params, string $method = 'post'): array
    {
        // 外卖模拟数据回调，不请求真实接口
        if (config('app.env') != 'prod' && config('waimai.testCallback')) {

            $whiteUrlList = [
                $this->host . '/api/v1/order/getOrderDetail',
            ];

            if (!in_array($url, $whiteUrlList)) {
                Log::debug("TEST CALLBACK 1");
                return $this->returnFormatData(true, '', '模拟外卖接口返回', []);
            }
        }

        // 公共参数
        $params += [
            'timestamp' => time(),
            'app_id'    => $this->app_id,
        ];

        // 签名
        $params['sig'] = $this->sign($url, $params);

        // 累加美团接口调用次数
        Redis::hIncrBy(WaiMaiCacheEnum::getMtApiCurlNum(), date('Ymd'), 1);

        $startMicroTime = microtime(true);

        if ($method == 'get') {
            $response = (new Client([
                'timeout' => self::TIME_OUT,
                'headers' => [],
            ]))->get($url, [RequestOptions::QUERY => $params]);
        } else {
            $response = (new Client([
                'timeout' => self::TIME_OUT,
                'headers' => [],
            ]))->post($url, [RequestOptions::FORM_PARAMS => $params]);
        }

        try {
            (new ApiTimeService('mtWm'))->add($url, $startMicroTime);
        } catch (\Exception $e) {
            Log::warning($e);
        }

        $uuid = md5(microtime(true) . rand(1, 10000));
        Log::info('MtWM Request:', ['uuid' => $uuid, 'url' => $url, ['params' => $params]]);
        $s = json_decode($response->getBody()->getContents(), true) ?? [];
        Log::info('MtWM Response:', ['uuid' => $uuid, 'data' => $s]);

        if (isset($s['error'])) {
            return $this->returnFormatData(false, $s['error']['code'] ?? 0, $s['error']['msg'] ?? '美团外卖接口异常',
                $s['data'] ?? []);
        }

        return $this->returnFormatData(true, '', '', $s['data'] ?? []);
    }

    /**
     * 生成签名
     * @param string $url
     * @param array $data
     * @return string
     */
    public function sign(string $url, array $data): string
    {
        $str = $url . '?';

        // 排序并添加
        ksort($data);
        foreach ($data as $k => $v) {
            $str .= $k . '=' . $v . '&';
        }
        $str = rtrim($str, '&');

        // 添加秘钥
        $str .= '' . $this->app_secret;

        return md5($str);
    }

    /**
     * 回调校验签名
     * @param Request $request
     * @param string $param
     * @return bool
     */
    public function checkSign(Request $request, array $param): bool
    {
        $url = config('other.GATEWAY_URL') . $request->getPathInfo();

        Log::info('MMMMMT URL:' . $url);

        foreach ($param as &$v) {
            $v = urldecode($v);
        }

        $paramSig = $param['sig'];
        unset($param['sig']);

        if ($paramSig == $this->sign($url, $param)) {
            return true;
        }

        return false;
    }
}
